/*
 * Copyright (C) 2014 Nathan T
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package engine.game.combat.ability;

import engine.entities.Mob;
import engine.game.Game;
import engine.game.Tickable;
import static engine.game.combat.ability.Ability.ABILITY_ANIM.NONE;
import engine.game.gfx.ColoredSprite;
import engine.game.gfx.Screen.ScreenSelectFilter;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.ImageIcon;
import javax.swing.JButton;

/**
 *
 * @author Nathan T
 */
public abstract class Ability implements Tickable {

    public static final int SCALE = 1;
    public static final int BACKGROUND_COLOR = (new JButton()).getBackground().hashCode();

    protected final String name;
    protected final ColoredSprite sprite;
    protected final ImageIcon icon;
    protected ABILITY_ANIM animType;

    protected boolean requiresMobTarget;
    protected boolean requiresPointTarget;
    protected boolean startsCombat;

    protected Game game;
    protected Mob caster;
    protected Mob target;
    protected Point targetPoint;

    protected boolean endsCombatTurn;

    protected long animStartTime;
    protected ColoredSprite animStartSprite;
    protected ColoredSprite animActionSprite;
    protected ColoredSprite animDefaultSprite;

    public Ability(String name, ColoredSprite sprite) {
        this.name = name;
        this.sprite = sprite;
        this.game = Game.getGlobalGame();
        this.icon = getImageIcon();
    }

    protected final ImageIcon getImageIcon() {
        BufferedImage bufferedIconImage = new BufferedImage(sprite.width(), sprite.height(), BufferedImage.TYPE_INT_RGB);
        int imagePixels[] = ((DataBufferInt) bufferedIconImage.getRaster().getDataBuffer()).getData();

        for (int i = 0; i < sprite.pixels().length; i++) {
            for (int j = 0; j < sprite.pixels()[0].length; j++) {
                if (sprite.pixels()[i][j] < 255) {
                    imagePixels[i + (j * sprite.width())] = Game.colors[sprite.pixels()[i][j]];
                }
                else {
                    imagePixels[i + (j * sprite.width())] = BACKGROUND_COLOR;
                }
            }
        }

        Image resizedImage = bufferedIconImage.getScaledInstance(sprite.width() * SCALE, sprite.height() * SCALE, Image.SCALE_SMOOTH);
        return new ImageIcon(resizedImage);
    }

    public String getName() {
        return name;
    }

    public ColoredSprite getSprite() {
        return sprite;
    }

    public ImageIcon getIcon() {
        return icon;
    }

    public boolean requiresMobTarget() {
        return this.requiresMobTarget;
    }

    public boolean requiresPointTarget() {
        return this.requiresPointTarget;
    }

    public void activateAbility(Mob caster, Mob target, Point targetPoint) {
        this.caster = caster;
        this.target = target;
        this.targetPoint = targetPoint;

        if (!this.hasAnimation()) {
            finishAbility();
        }
        else {
            this.animStartTime = System.currentTimeMillis();
            this.animStartSprite = caster.getActiveSprite();
            switch (this.animType) {
                case ATTACK:
                    if (target != null) {
                        caster.setFacing(caster.getDirectionTowards(target));
                    }
                    this.animActionSprite = caster.getAttackSprite(caster.getFacing());
                    this.animDefaultSprite = caster.getFaceSprite();
                    break;
                case CAST:
                    this.animActionSprite = caster.getCastSprite();
                    this.animDefaultSprite = caster.getFrontFaceSprite();
                    break;
            }
            Game.addTickable(this);
        }
    }

    protected void finishAbility() {
        if (startsCombat()) {
            if (!caster.isInCombat() && !target.isInCombat()) {
                caster.startCombat(target);
            }
        }

        payCosts();
        activateEffect();

        if (endsCombatTurn()) {
            caster.endCombatTurn();
        }

        Game.getGlobalGame().requestFrameFocus();
    }

    @Override
    public void tick() {
        updateAnimStatus();
    }

    protected void updateAnimStatus() {
        double delta = (System.currentTimeMillis() - animStartTime) / 250.0;
        if (delta > 0.2 && delta < 0.8) {
            caster.setActiveSprite(animActionSprite);
        }
        else if (delta > 1) {
            caster.setActiveSprite(animStartSprite);
            Game.removeTickable(this);
            finishAbility();
        }
        else {
            caster.setActiveSprite(animDefaultSprite);
        }
    }

    public boolean canPayCosts(Mob caster) {
        boolean canPay = true;
        canPay = canPay && (caster.getHealth().getValue() > getHealthCost());
        canPay = canPay && (caster.getMana().getValue() >= getManaCost());
        canPay = canPay && (caster.getStamina().getValue() >= getStaminaCost());
        canPay = canPay && (caster.getAether().getValue() >= getAetherCost());
        return canPay;
    }

    protected void payCosts() {
        caster.payHealthCost(getHealthCost());
        caster.payStaminaCost(getStaminaCost());
        caster.payManaCost(getManaCost());
        caster.payAetherCost(getAetherCost());
    }

    protected void activateEffect() {

    }

    protected int getHealthCost() {
        return 0;
    }

    protected int getStaminaCost() {
        return 0;
    }

    protected int getManaCost() {
        return 0;
    }

    protected int getAetherCost() {
        return 0;
    }

    public boolean endsCombatTurn() {
        return this.endsCombatTurn;
    }

    public String getToolTipString() {
        String tip = "";
        tip += "<html>";
        tip += "<b>" + this.getName() + ":</b>";

        tip += "<br>Cost: ";
        tip += costString();

        tip += "<br>Effect: ";
        tip += effectString();
        tip += "</html>";

        return tip;
    }

    public boolean startsCombat() {
        return startsCombat;
    }

    public boolean hasAnimation() {
        return (this.animType != NONE);
    }

    public abstract String costString();

    public abstract String effectString();

    public abstract ScreenSelectFilter getMobFilter();

    public abstract ScreenSelectFilter getPointFilter();

    public enum ABILITY_ANIM {

        CAST,
        ATTACK,
        NONE
    }

}
